package com.clover.video.ffmpeg;

import com.clover.video.VideoInfo;
import com.clover.video.utils.RegexUtils;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class FfmpegUtil {

    public static void main(String[] args) {
        String inputMp4 = "D:\\video\\55.mp4";
        String ffmpegPath = "D:\\JavaTools\\ffmpeg.exe";

        try {
            //System.out.println(findVideoInfo(ffmpegPath, url, true));

            long start = System.currentTimeMillis();


            /*String cmdStr = ffmpegPath + " -i D:\\video\\55.mp4 -c:v libx264 -hls_time 60 -hls_list_size 0 -c:a aac -strict -2 -f hls -hls_enc 1 -hls_enc_key 123 D:\\video\\55\\55.m3u8";
            if (process(cmdStr)) {
            }*/
            //mp4 压缩
            //compressMP4(ffmpegPath, inputMp4, inputMp4.replace(".mp4","-1.mp4"), true);
            //mp4 转 m3u8
            m3u8(ffmpegPath, inputMp4, "D:\\video\\55\\55_","123");
            //m3u81(ffmpegPath, inputMp4, "D:\\video\\55\\55_");

            long end = System.currentTimeMillis();
            System.out.println("转换耗时：" + (end - start) / 1000 + " 秒");
        } catch (Exception ex) {
            ex.printStackTrace();
            //System.out.println(ex.toString());
        }
    }

    /**
     * 获取视频文件信息
     * @Author masl - 2019/8/20 0020 下午 15:57
     * @param inputPath
     * @return
     */
    private static VideoInfo findVideoInfo(String ffmpegPath, String inputPath, boolean printMsg) {
        List<String> commend = new java.util.ArrayList<String>();
        commend.add(ffmpegPath);
        commend.add("-i");
        commend.add(inputPath);

        VideoInfo videoInfo = new VideoInfo();
        try {
            Process p = new ProcessBuilder()
                    .command(commend)
                    .redirectErrorStream(true)
                    .start();

            BufferedReader buf = new BufferedReader(new InputStreamReader(p.getInputStream()));// 保存ffmpeg的输出结果流
            String line = null;
            StringBuffer sb = new StringBuffer();
            while ((line = buf.readLine()) != null) {
                //System.out.println(line);
                sb.append(line);
                continue;
            }
            p.waitFor();//这里线程阻塞，将等待外部转换进程运行成功运行结束后，才往下执行

            String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
            String regexVideo = "Video: (.*?), (.*?), (\\d+x\\d+)";
            String regexAudio = "Audio: (\\w*).*?(\\d*) Hz";

            List<String[]> arr = new ArrayList<>();//RegexUtils.regMatchGroupList(regexDuration, sb.toString(),new int[]{1,2,3})
            if (RegexUtils.match(regexDuration, sb.toString())) {
                arr = RegexUtils.regMatchGroupList(regexDuration, sb.toString(), new int[]{1, 2, 3});

                videoInfo.setPlayTime(arr.get(0)[0]);
                videoInfo.setStartTime(arr.get(0)[1]);
                videoInfo.setBitRate(Integer.valueOf(arr.get(0)[2]));
            }
            if (RegexUtils.match(regexVideo, sb.toString())) {
                arr = RegexUtils.regMatchGroupList(regexVideo, sb.toString(), new int[]{1, 2, 3});

                videoInfo.setCodeType(arr.get(0)[0]);
                videoInfo.setVideoType(arr.get(0)[1]);
                videoInfo.setResolvingPowerW(Integer.valueOf(arr.get(0)[2].split("x")[0]));
                videoInfo.setResolvingPowerH(Integer.valueOf(arr.get(0)[2].split("x")[1]));
            }
            if (RegexUtils.match(regexAudio, sb.toString())) {
                arr = RegexUtils.regMatchGroupList(regexAudio, sb.toString(), new int[]{1, 2});

                videoInfo.setAudioCodec(arr.get(0)[0]);
                videoInfo.setAudioFreq(Integer.valueOf(arr.get(0)[1]));
            }
            if (printMsg) {
                System.out.println("提取出播放时间--->" + videoInfo.getPlayTime());
                System.out.println("开始时间--->" + videoInfo.getStartTime());
                System.out.println("bitrate 码率 单位 kb--->" + videoInfo.getBitRate());

                System.out.println("编码格式--->" + videoInfo.getCodeType());
                System.out.println("视频格式--->" + videoInfo.getVideoType());
                System.out.println(" 分辨率--->" + videoInfo.getResolvingPowerW() + " x " + videoInfo.getResolvingPowerH());

                System.out.println("音频编码--->" + videoInfo.getAudioCodec());
                System.out.println("音频采样频率--->" + videoInfo.getAudioFreq());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return videoInfo;
    }

    public static void m3u81(String ffmpegPath, String inputPath, String outputPath) throws FFmpegException {


        List command = new ArrayList();

        /*command.add(ffmpegPath);
        command.add("-y");
        command.add("-i");
        command.add(inputPath);
        command.add("-vcodec");
        command.add("copy");
        command.add("-acodec");
        command.add("copy");
        command.add("-vbsf");
        command.add("h264_mp4toannexb");
        command.add(outputPath + ".ts");
        if (process(command)) {
        }

        inputPath = outputPath + ".ts";*/
        command.clear();
        command.add(ffmpegPath);
        command.add("-i");
        command.add(inputPath);
        command.add("-c:v");
        command.add("libx264");
        command.add("-f");
        command.add("hls");
        command.add("-hls_list_size");
        command.add("0");
        //command.add("-hls_enc");
        //command.add("1");
        command.add("-hls_time");
        command.add("60");
        command.add("-hls_enc_key");
        command.add("ASKIJDENFODJFNRJ");
        //command.add("-hls_enc_key_url");
        //command.add("https://pub-video.oss-cn-qingdao.aliyuncs.com/password");
        command.add(inputPath.replace(".ts",".m3u8"));
        if(process(command)){
        }
    }

    public static String m3u8(String ffmpegPath, String inputPath, String outputPath, String pwd) throws FFmpegException {
        if (!checkfile(inputPath)) {
            File file = new File(inputPath);
        }
        VideoInfo videoInfo = findVideoInfo(ffmpegPath, inputPath, true);

        //ffmpeg -i foo.mp4 -codec copy -vbsf h264_mp4toannexb -map 0 -f segment -segment_list out.m3u8 -segment_time 10 out%03d.ts
        //ffmpeg -i 2.mp4 -c:v libx264 -c:a aac -hls_list_size 0 -hls_time 10 -g 0 -strict -2 -f hls output.m3u8
        //ffmpeg -i test.mp4 -c:v libx264 -c:a aac -strict -2  -hls_time 15 -hls_list_size 0 test.m3u8
        //String cmdStr = @" -i D:\video\02.mp4 -c:v libx264 -hls_time 10 -hls_list_size 0 -c:a aac -strict -2 -f hls D:\video\02\ML.m3u8";
        StringBuilder cmdStr = new StringBuilder();

        cmdStr.append(ffmpegPath);
        cmdStr.append(" -i " + inputPath);
        cmdStr.append(" -c:v libx264 ");
        cmdStr.append(" -c:a aac ");
        cmdStr.append(" -hls_time 60 ");//每片时长 秒
        cmdStr.append(" -hls_list_size 0 ");//最多保存条目，0：所有，默认为 5
        cmdStr.append(" -strict -2 ");
        cmdStr.append(" -f hls ");
        if (pwd != null && pwd.length() > 0)
            cmdStr.append(" -hls_enc 1 -hls_enc_key " + pwd);//加密
        //cmdStr.append(" -b " + videoInfo.getBitRate() + "k ");//比真实的大？
        cmdStr.append(" -preset:v fast "); // 默认为：medium
        cmdStr.append(outputPath + ".m3u8");
        if (cmdStr.length() > 0 && process(cmdStr.toString())) {
            //File file = new File(inputPath);
            //file.delete();
            return outputPath + ".m3u8";
        }
        return null;
    }

    /**
     * mp4文件压缩
     * @param ffmpegPath
     * @param inputPath
     * @param outputPath
     * @return：是否需要压缩
     * @throws FFmpegException
     */
    public static boolean compressMP4(String ffmpegPath, String inputPath, String outputPath, boolean printMsg) throws FFmpegException {
        if (!checkfile(inputPath)) {
            throw new FFmpegException("非文件");
        }
        boolean isCompress = false;
        VideoInfo videoInfo = findVideoInfo(ffmpegPath, inputPath, false);

        StringBuilder cmdStr = new StringBuilder();
        //String cmdStr1 = @"-y -i D:\video\02.mp4 -b 768k -r 25 -s 720*576 -vcodec copy D:\video\02-1.mp4";
        //可以调低一点码率，比如调一下crf参数，这样会快一点
        cmdStr.append(ffmpegPath);
        cmdStr.append(" -y ");
        cmdStr.append(" -i " + inputPath);

        //cmdStr.append(" -c:v libx265 ");
        cmdStr.append(" -c:v libx264 ");//编码  -c:v libx265（x265虽然好，但是编码实在是太慢了）
        cmdStr.append(" -preset:v faster "); //ultrafast、superfast、veryfast、faster、fast、medium（默认值）、slow、slower、veryslow、placebo从快到慢

        //视频流量(码率) -- 高清即可
        if (videoInfo.getBitRate() > BitrateType.HIGH_CLEAR.getBitRate()) {
            isCompress = true;
            cmdStr.append(" -b " + BitrateType.HIGH_CLEAR.getBitRate() + "k ");
        }
        //常见的分辨率有4096*2304,1920*1080,720*576等
        if (videoInfo.getResolvingPowerW() > videoInfo.getResolvingPowerH() && videoInfo.getResolvingPowerW() > 960) {
            isCompress = true;
            cmdStr.append(" -vf scale=960:" + videoInfo.getResolvingPowerH() + "/(" + videoInfo.getResolvingPowerW() + "*1.00/960) "); // TODO 经测试 1920*1080 压缩宽 720 等比时会报错
            //int h = w * videoInfo.getResolvingPowerH() / videoInfo.getResolvingPowerW();
            //cmdStr.append(" -s " + w + "*" + h);//设定画面的宽与高
            //cmdStr.append(" -vf scale=720:-1 ");//过滤器 -- 错误
        }
        if (videoInfo.getResolvingPowerW() < videoInfo.getResolvingPowerH() && videoInfo.getResolvingPowerH() > 540) {
            isCompress = true;
            //-vf scale={0}/({1}*1.00/540):540
            cmdStr.append(" -vf scale=" + videoInfo.getResolvingPowerW() + "/(" + videoInfo.getResolvingPowerH() + "*1.00/540):540");
        }
        if (!isCompress) {
            if (printMsg)
                System.out.println(inputPath + " 该视频文件不需要压缩");
            return false;
        }
        //cmdStr.append(" -qp 0 ");//无损压缩  不能与 vf 同时存在
        cmdStr.append(" -r 25 ");//设定帧速率，默认为25
        cmdStr.append(" -bitexact ");//-bitexact 使用标准比特率
        cmdStr.append(outputPath);

        if (printMsg)
            System.out.println("视频压缩命令---->" + cmdStr);

        if (process(cmdStr.toString())) {
            System.out.println(inputPath + " -- 压缩完毕！");
        }
        return true;
    }

    private static boolean checkfile(String path) {
        File file = new File(path);
        if (!file.isFile()) {
            return false;
        }
        return true;
    }

    private static boolean process(List command) throws FFmpegException{
        int n = 0;
        try {

            if (null == command || command.size() ==0) {
                return false;
            }
            Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();

            new PrintStream(videoProcess.getErrorStream()).start();

            int exitcode = videoProcess.waitFor();

            if (exitcode == 1) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed",e);
        }

    }

    private static boolean process(String commandStr) throws FFmpegException{
        try {
            if (commandStr == null || commandStr.length() == 0) {
                return false;
            }
            Process p = Runtime.getRuntime().exec(commandStr);
            new PrintStream(p.getErrorStream()).start();
            int exitcode = p.waitFor();

            if (exitcode == 1) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new FFmpegException("file uploadfailed", e);
        }
    }
}